using UnityEditor;
using UnityEngine;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System;


public class MeshMergeEditor : UnityEditor.EditorWindow
{
    [MenuItem("Tools/模型合并工具")]
    public static void ShoweWindow()
    {
        var win = GetWindow<MeshMergeEditor>();
        win.Show();
    }

    /*
    [MenuItem("Tools/TestOptUV")]
    public static void TestUV()
    {

        List<int> tri = new List<int>(){
            1,2,3,
            5,6,7,
            3,4,8,
            8,9,10,
            7,11,12,
            13,16,18
        };

        var uvShells = SubRendererData.UVShell.SeparateUV(tri);
        foreach(var shell in uvShells)
        {
            StringBuilder sb = new StringBuilder();
            foreach(var i in shell.triangles)
            {
                sb.Append(i);
                sb.Append(',');
            }

            Debug.Log(sb.ToString());
        }
                
    }*/

    public bool includeUV2 = false;

    public bool usbPowOfTow = true;
    public bool mergeSubUV = true;
    public bool useSingleLine = false;
    public bool optimizeUV = true;
    public Mesh currMesh = null;

    private MeshMerge.MeshMergeSpace mergeSpace = MeshMerge.MeshMergeSpace.WorldSpace;
    private Texture previewTex = null;
    private GameObject lastSelectObj = null;

    private List<Vector2> cacheUVs = new List<Vector2>();
    private List<Color> uvShellColors = new List<Color>();
    private List<int> uvShellSize = new List<int>();

    private string outputPath = "Assets/Art/CombineModels";
    
    private void OnEnable()
    {
        Selection.selectionChanged += Selected;
    }

    private void OnDisable()
    {
        Selection.selectionChanged -= Selected;
    }
    public void Selected()
    {
        GameObject curObj = Selection.activeGameObject;
        if(curObj!=null)
        {
            MeshFilter mf = curObj.GetComponent<MeshFilter>();
            Renderer renderer = curObj.GetComponent<MeshRenderer>();

            if(mf!=null)
                currMesh = mf.sharedMesh;
            if(renderer!=null && renderer.sharedMaterial && renderer.sharedMaterial.mainTexture)
            {
                previewTex =renderer.sharedMaterial.mainTexture;
            }
            else
            {
                
            }
            lastSelectObj =curObj;

            CalCacheUVData();
        }

        //刷新GUI
        this.Repaint();
    }
    public void OnGUI()
    {

        GUILayout.Label("输出文件夹:");
        outputPath = GUILayout.TextField(outputPath);

        GUILayout.BeginHorizontal();

        GUILayout.Label("PowOfTow:");
        usbPowOfTow = EditorGUILayout.Toggle(usbPowOfTow);

        GUILayout.Label("相同贴图合并UV:");
        mergeSubUV = EditorGUILayout.Toggle(mergeSubUV);

        GUILayout.Label("优化UV:");
        optimizeUV = EditorGUILayout.Toggle(optimizeUV);

        //独享一行和扩充有冲突
        //GUILayout.Label("高重复度时单独使用一行:");
        //useSingleLine = EditorGUILayout.Toggle(useSingleLine);

        //实验
        //GUILayout.Label("包含UV2:");
        //includeUV2 = EditorGUILayout.Toggle(includeUV2);

        GUILayout.EndHorizontal();

        GUILayout.Label("合并空间:");
        mergeSpace = (MeshMerge.MeshMergeSpace)EditorGUILayout.EnumPopup(mergeSpace);


        if(GUILayout.Button("处理选中的模型") && Selection.activeGameObject!=null)
        {
            var projectPath = System.IO.Path.GetDirectoryName(Application.dataPath);

            var outputAsseetPath = System.IO.Path.Combine(outputPath);
            if(!System.IO.Directory.Exists(outputAsseetPath))
            {
                System.IO.Directory.CreateDirectory(outputAsseetPath);
            }

            MeshMerge.MeshMergeFlags flags = MeshMerge.MeshMergeFlags.None;

            if(optimizeUV)
            flags |= MeshMerge.MeshMergeFlags.OptimizeUV;
            
            if(mergeSubUV)
            flags |= MeshMerge.MeshMergeFlags.MergeSubUV;

            
            if(useSingleLine)
            flags |= MeshMerge.MeshMergeFlags.UseSingleLine;
            
            
            if(usbPowOfTow)
            flags |= MeshMerge.MeshMergeFlags.PowerOfTwo;

            
             if(includeUV2)
            flags |= MeshMerge.MeshMergeFlags.IncludeUV2;

            EditorUtility.DisplayProgressBar("收集模型数据...","等待...",0);

            MeshMerge meshMerge =  MeshMerge.GenerateMeshMerge(Selection.activeGameObject,flags,mergeSpace);
            EditorUtility.DisplayProgressBar("开始处理中...","等待...",0.5f);
            meshMerge.MergeProcess();
            previewTex = meshMerge.mergeRT;
            EditorUtility.DisplayProgressBar("创建资源中...","等待...",0.5f);
            CreateAssets(meshMerge);
            EditorUtility.ClearProgressBar();
        }
        

        //if(previewTex!=null)
        {

            GUILayout.Space(20);

            float width = position.width*0.75f;

            var lastRect = GUILayoutUtility.GetLastRect();
            
            Rect rect = GUILayoutUtility.GetRect(width,width);

            rect.x = rect.width*0.5f-rect.width*0.75f*0.5f;
            rect.width = rect.width*0.75f;

        
            Rect edgeRect = rect;
            edgeRect.x-=2;
            edgeRect.y-=2;
            edgeRect.width+=4;
            edgeRect.height+=4;
            Handles.DrawSolidRectangleWithOutline(edgeRect, Color.black, Color.blue*0.5f);

            if(previewTex!=null)
            GUI.DrawTexture(rect,previewTex);

            Matrix4x4 matrix = Handles.matrix;

            Matrix4x4 matrix2 = new Matrix4x4();
            matrix2.SetTRS(new Vector3(rect.x,rect.height+rect.y),Quaternion.identity,new Vector3(rect.width,-rect.height,1));
            Handles.matrix = matrix2;
            Handles.color = new Color(1,1,0,0.2f);

            if(uvShellColors.Count==0)
            {
                for(var idx=0;idx<cacheUVs.Count;idx+=3)
                {
                    var a = cacheUVs[idx];
                    var b = cacheUVs[idx+1];
                    var c = cacheUVs[idx+2];

                    Handles.DrawLine(a,b);
                    Handles.DrawLine(b,c);
                    Handles.DrawLine(c,a);
                }
            }
            else{
                for(var idx=0;idx<uvShellColors.Count;idx++)
                {
                    Handles.color = uvShellColors[idx];

                    int sindex = 0;
                    if(idx>0)
                        sindex = uvShellSize[idx-1];

                    int endindex = uvShellSize[idx];

                     for(var tidx=sindex;tidx<endindex;tidx+=3)
                     {
                        var a = cacheUVs[tidx];
                        var b = cacheUVs[tidx+1];
                        var c = cacheUVs[tidx+2];

                        Handles.DrawLine(a,b,1);
                        Handles.DrawLine(b,c,1);
                        Handles.DrawLine(c,a,1);
                     }
                }
            }
            Handles.matrix = matrix;
        }
    }

    private void CalCacheUVData()
    {
        if(currMesh==null)
            return;

        cacheUVs.Clear();

        uvShellColors.Clear();
        uvShellSize.Clear();

        if(currMesh.uv.Length!=currMesh.vertices.Length)
        {
            return;
        }

        if(currMesh.triangles.Length>16384)
        {
            Debug.LogWarning("显示那么大的模型可能是卡顿");
            return;
        }

       var uvShells = SubRendererData.UVShell.SeparateUV(currMesh.triangles.ToList());
       
        foreach(var uvShell in uvShells )
        {
            foreach(var tri in uvShell.triangles)
            {
                cacheUVs.Add(currMesh.uv[tri]);
            }
            uvShellSize.Add(cacheUVs.Count);
            uvShellColors.Add(new Color(UnityEngine.Random.value,UnityEngine.Random.value,UnityEngine.Random.value,0.5f));
        }
    }

    private void CreateAssets(MeshMerge meshMerge)
    {

        GameObject newGo = new GameObject();

        //URP
       //newGo.AddComponent<MeshRenderer>().material = new Material(Shader.Find());

       string shadeName = "Universal Render Pipeline/Lit";
       //shadeName ="Standard";

       Texture2D tex = RenderTexutreUtils.ToTexture(meshMerge.mergeRT);

       newGo.name = meshMerge.name;

       newGo.transform.position = meshMerge.rootTransform.position;
       newGo.transform.rotation = meshMerge.rootTransform.rotation;
       newGo.transform.localScale = meshMerge.rootTransform.localScale;

       string assetName = newGo.name;
       
       string projectPath = System.IO.Path.GetDirectoryName(Application.dataPath);

       string imgSuffix;
       imgSuffix = "_tex.png";
       //imgSuffix = "_tex.tga";

       string texPath = String.Format("{0}/{1}/{2}{3}",projectPath,outputPath,assetName,imgSuffix);
       string meshPath = String.Format("{0}/{1}",outputPath,assetName+"_mesh.asset");
       string matPath =  String.Format("{0}/{1}",outputPath,assetName+"_mat.mat");

        byte[] texBytes = null ;
        
        if(imgSuffix=="_tex.png")
        {
            texBytes = tex.EncodeToPNG();
        }
        else if( imgSuffix ==  "_tex.tga")
        {
            texBytes = tex.EncodeToTGA();
        }

        System.IO.File.WriteAllBytes(texPath,texBytes);
        AssetDatabase.Refresh();

        Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
        if(mat==null)
        {
            mat =  new Material(Shader.Find(shadeName));
            AssetDatabase.CreateAsset(mat,matPath);
        }

        newGo.AddComponent<MeshRenderer>().material = mat;

        mat.mainTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(outputPath+"/"+assetName+imgSuffix);

        var allVertices = meshMerge.allVertices;
        var allTriangles = meshMerge.allTriangles;
        var allNormals = meshMerge.allNormals;
        var allUvs = meshMerge.allUvs;

       Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(meshPath);
       if(mesh==null)
       {
            mesh = new Mesh();
            mesh.SetVertices(allVertices);
            mesh.SetTriangles(allTriangles.ToArray(),0);
            mesh.SetNormals(allNormals);
            mesh.SetUVs(0,allUvs);
            
            if(meshMerge.allUv2.Count>=0)
            mesh.SetUVs(1,meshMerge.allUv2);
            AssetDatabase.CreateAsset(mesh,meshPath);
       }
       else
       {
            mesh.SetVertices(allVertices);
            mesh.SetTriangles(allTriangles.ToArray(),0);
            mesh.SetNormals(allNormals);
            mesh.SetUVs(0,allUvs);

            if(meshMerge.allUv2.Count>=0)
            mesh.SetUVs(1,meshMerge.allUv2);
       }
       mesh.RecalculateTangents();
       mesh.RecalculateNormals();
       mesh.RecalculateBounds();
       newGo.AddComponent<MeshFilter>().mesh = mesh;

       bool success;
       PrefabUtility.SaveAsPrefabAsset(newGo,outputPath+"/" +assetName+".prefab",out success);
       if(success)
       {
            AssetDatabase.Refresh();
       }
        AssetDatabase.SaveAssets();
    }
}
